#include "../Connector.h"
#include "../Channel.h"
#include "../EventLoop.h"
using namespace yhaida;

const int Connector::kMaxRetryDelayMs = 30 * 1000;
const int Connector::kInitRetryDelayMs = 500;

Connector::Connector(EventLoop *loop, const InetAddress &serverAddr)
    : _mainEventLoop(loop),
      _serverAddr(serverAddr),
      _start(false),
      _state(States::kDisconnected),
      _retryDelayMs(Connector::kInitRetryDelayMs)
{
    LOG_DEBUG << "ctor[" << this << "]";
}

Connector::~Connector()
{
    LOG_DEBUG << "dtor[" << this << "]";
    _mainEventLoop->cancel(_timeID);
    assert(!_channel);
}

void Connector::start()
{
    _start = true;
    LOG_DEBUG;
    _mainEventLoop->runInLoop(std::bind(&Connector::startInLoop, this));
}
void Connector::startInLoop()
{
    _mainEventLoop->assertInLoopThread();
    assert(_state == kDisconnected);
    if (_start)
    {
        connect();
    }
    else
    {
        LOG_DEBUG << "do not connect";
    }
}
void Connector::connect()
{
    int sockfd = sockets::createNonblockingOrDie();
    int ret = sockets::connect(sockfd, _serverAddr.getSockAdrrInet());
    int savedErrno = (ret == 0) ? 0 : errno;
    switch (savedErrno)
    {
    case 0:
    case EINPROGRESS:
    case EINTR:
    case EISCONN:
        connecting(sockfd);
        break;

    case EAGAIN:
    case EADDRINUSE:
    case EADDRNOTAVAIL:
    case ECONNREFUSED:
    case ENETUNREACH:
        retry(sockfd);
        break;

    case EACCES:
    case EPERM:
    case EAFNOSUPPORT:
    case EALREADY:
    case EBADF:
    case EFAULT:
    case ENOTSOCK:
        LOG_SYSERR << "connect error in Connector::startInLoop " << savedErrno;
        sockets::close(sockfd);
        break;

    default:
        LOG_SYSERR << "Unexpected error in Connector::startInLoop " << savedErrno;
        sockets::close(sockfd);
        // connectErrorCallback_();
        break;
    }
}
void Connector::connecting(int socket)
{
    setState(States::Kconnecting);
    assert(!_channel);
    _channel.reset(new Channel(_mainEventLoop, socket));
    _channel->setWriteCallBackFuc(std::bind(&Connector::handleWrite, this));
    _channel->setErrorCallBackFuc(std::bind(&Connector::handleError, this));

    _channel->enableWriting();//人为触发write信号
}
//重新连接是要先关闭之前fd
void Connector::retry(int socket)
{
    sockets::close(socket);
    setState(States::kDisconnected);
    if (_start)
    {
        LOG_INFO << "Connector::retry - Retry connectinng to"
                 << _serverAddr.toHostPort() << " in "
                 << _retryDelayMs << "milliseconds.";

        _timeID = _mainEventLoop->runAfter(_retryDelayMs / 1000.0,
                                           std::bind(&Connector::startInLoop, this));
        //间隔增长延迟时间算法
        _retryDelayMs = std::min(_retryDelayMs * 2, kMaxRetryDelayMs);
    }
    else
    {
        LOG_DEBUG << "do not connect";
    }
}

void Connector::restart()
{
    _mainEventLoop->assertInLoopThread();
    setState(States::kDisconnected);
    _retryDelayMs = kInitRetryDelayMs;
    _start = true;
    startInLoop();
}

void Connector::stop()
{
    _start = false;
    _mainEventLoop->cancel(_timeID);
}

void Connector::handleWrite()
{
    //注册connector的fd时候有可能出现一些错误，所以在下面进行分析
    LOG_TRACE << "Connector::handleWrite " << _state;
    if (_state == Kconnecting)
    {
        int sockfd = removeAndResetChannel();
        int err = sockets::getSocketError(sockfd);
        if (err)
        {
            LOG_WARN << "Connector::handleWrite - SO_ERROR = "
                     << err << " " << strerror_tl(err);
            retry(sockfd);
        }
        else if (sockets::isSelfConnect(sockfd))
        {
            LOG_WARN << "Connector::handleWrite - Self connect";
            retry(sockfd);
        }
        else
        {
            setState(States::Kconnected);
            if (_start)
            {
                _newConnectionCallFunction(sockfd);
            }
            else
            {
                sockets::close(sockfd);
            }
        }
    }
    else
    {
        assert(_state == kDisconnected);
    }
}
void Connector::handleError()
{
    LOG_ERROR << "Connector::handleError";
    assert(_state == Kconnecting);
    int sockfd = removeAndResetChannel();
    int err = sockets::getSocketError(sockfd);
    LOG_TRACE << "SO_ERROR = " << err << " " << strerror_tl(err);
    retry(sockfd);
}
//建立连接和未建立连接都得删除，EventLoop相当于提供一个队列
int Connector::removeAndResetChannel()
{
    _channel->disenableAll();
    int sockfd = _channel->fd();
    _mainEventLoop->removeChannel(get_pointer(_channel));

    _mainEventLoop->queueInLoop(std::bind(&Connector::resetChannel, this));
    return sockfd;
}

void Connector::resetChannel()
{
    _channel.reset(); // asser(!_channel);
}
